﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Collections.Specialized;

namespace DSLFactory.VSCTDesigner
{
    class VSCTLibraryWrapper : IVSCTCommandTable
    {
        private string[] _sdkIncludeFolder;
        private object _commandTableInstance;

        /// <summary>
        /// Initializes a new instance of the <see cref="VSCTLibraryWrapper"/> class.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        public VSCTLibraryWrapper(string fileName)
        {
            _sdkIncludeFolder = new string[2];
            _sdkIncludeFolder[0] = Path.Combine(ShellHelper.GetSDKInstallationPath(), @"VisualStudioIntegration\Common\inc");
            _sdkIncludeFolder[1] = Path.Combine(_sdkIncludeFolder[0], "office10");
   
            try
            {
                ImportSymbols(fileName);
            }
            catch { }
        }

        /// <summary>
        /// Imports the symbols.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        private void ImportSymbols( string fileName)
        {
            string initialDirectory = Environment.CurrentDirectory;
            try
            {
                Assembly asm = LoadVSCTLibrary();
                if( asm != null )
                {
                    Environment.CurrentDirectory = Path.GetDirectoryName(fileName);

                    Type t = asm.GetType("Microsoft.VisualStudio.CommandTable.CommandTable");
                    _commandTableInstance = Activator.CreateInstance(t);
                    if( _commandTableInstance != null )
                    {
                        CallAddAdditionalIncludes( _sdkIncludeFolder);
                        StringCollection sc = new StringCollection();
                        sc.Add("__CTC__");
                        sc.Add("_CTC_GUIDS_");
                        CallAddAdditionalPreprocessorDefines(sc);
                        CallRead( fileName );
                    }
                }
            }
            finally
            {
                Environment.CurrentDirectory = initialDirectory;
            }
        }

        private void CallAddAdditionalPreprocessorDefines(StringCollection sc)
        {
            MethodInfo mi = _commandTableInstance.GetType().GetMethod("AddAdditionalPreprocessorDefines", BindingFlags.Instance | BindingFlags.Public);
            mi.Invoke(_commandTableInstance, new object[] { sc });
        }

        /// <summary>
        /// Calls the method.
        /// </summary>
        /// <param name="files">The files.</param>
        /// <returns></returns>
        private object CallAddAdditionalIncludes( string[] files)
        {
            MethodInfo mi = _commandTableInstance.GetType().GetMethod("AddAdditionalIncludes", BindingFlags.Instance | BindingFlags.Public);
            return mi.Invoke(_commandTableInstance, new object[] {files});
        }

        /// <summary>
        /// Calls the method.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        private object CallRead( string fileName)
        {
            Type t = _commandTableInstance.GetType();
            MethodInfo mi = t.GetMethod("Read", new Type[] { typeof(string), t.Assembly.GetType("Microsoft.VisualStudio.CommandTable.IMessageProcessor") });
            return mi.Invoke(_commandTableInstance, new object[] { fileName, null });
        }

        /// <summary>
        /// Calls the method.
        /// </summary>
        /// <returns></returns>
        private string[] CallGetGuidSymbols()
        {
            MethodInfo mi = _commandTableInstance.GetType().GetMethod("GetGuidSymbols", BindingFlags.Instance | BindingFlags.Public);
            return (string[])mi.Invoke(_commandTableInstance, null);
        }

        /// <summary>
        /// Calls the method.
        /// </summary>
        /// <param name="guid">The GUID.</param>
        /// <returns></returns>
        private string[] CallGetIDSymbols(Guid guid)
        {
            MethodInfo mi = _commandTableInstance.GetType().GetMethod("GetIDSymbols", BindingFlags.Instance | BindingFlags.Public);
            return (string[])mi.Invoke(_commandTableInstance, new object[] {guid});
        }

        /// <summary>
        /// Calls the method.
        /// </summary>
        /// <param name="guidName">Name of the GUID.</param>
        /// <returns></returns>
        private Guid CallFindSymbolGuid(string guidName)
        {
            MethodInfo mi = _commandTableInstance.GetType().GetMethod("FindSymbolGuid", BindingFlags.Instance | BindingFlags.Public);
            return (Guid)mi.Invoke(_commandTableInstance, new object[] { guidName });
        }

        /// <summary>
        /// Loads the VSCT library.
        /// </summary>
        /// <returns></returns>
        private Assembly LoadVSCTLibrary()
        {
            string path = Path.Combine(ShellHelper.GetSDKToolsPath(), "VSCTLibrary.dll");
            if( File.Exists(path) )
                return Assembly.LoadFile(path);
            return null;
        }

        /// <summary>
        /// Gets the GUID symbols.
        /// </summary>
        /// <returns></returns>
        public string[] GetGuidSymbols()
        {
            if( _commandTableInstance == null )
                return new string[0];
            return CallGetGuidSymbols();
        }

        /// <summary>
        /// Gets the ID symbols.
        /// </summary>
        /// <param name="guid">The GUID.</param>
        /// <returns></returns>
        public string[] GetIDSymbols(Guid guid)
        {
            if( _commandTableInstance == null )
                return new string[0];
            return CallGetIDSymbols(guid);
        }

        /// <summary>
        /// Finds the symbol GUID.
        /// </summary>
        /// <param name="guidName">Name of the GUID.</param>
        /// <returns></returns>
        public Guid FindSymbolGuid(string guidName)
        {
            if( _commandTableInstance == null )
                return Guid.Empty;
            return CallFindSymbolGuid(guidName);
        }
    }
}
